package com.common.android.utils.concurrent;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import android.util.Log;

/**
 * @author caimk
 * 
 *         这个类用来实现对线程池的统一管理,除非特殊情况，不然不要直接用使用NdThreadPoolExecutor类去生成线程池
 *         单独生成的线程池必须要在注销或者切换帐号的时候去销毁，并在再次启动
 * 
 */
public class NdExecutorService {
	private NdThreadPoolExecutor mTinyHttpThreadPool; // 网络小数据请求线程池
	private NdThreadPoolExecutor mCachedThreadPool; // 本地缓存数据请求线程池
	private NdThreadPoolExecutor mFileUploadDownloadThreadPool; // 文件上传下载操作线程池
	private NdThreadPoolExecutor mBackgroundThreadPool; // 后台数据同步线程池
	private LinkedBlockingQueue<Runnable> mCacheWorkQueue = new LinkedBlockingQueue<Runnable>();
	private PriorityBlockingQueue<Runnable> mTinyHttplWorkQueue = new PriorityBlockingQueue<Runnable>(
			);
	private ArrayBlockingQueue<Runnable> mArrayBlockWorkQueue = new ArrayBlockingQueue<Runnable>(
			128);

	
	private boolean mIsQuit = false;

	/*
	 * 停止接受新的请求，等待任务执行完成的超时时间，单位ms. 具体查询 {@link #shutdown shutdown}.
	 */
	public static final int AWAIT_TERMINATION_SELF = 3000;

	/*
	 * 强制停止，等待正在执行的任务完成的超时时间，单位ms. 具体查询 {@link #shutdown shutdown}.
	 */
	public static final int AWAIT_TERMINATION_FORCE = 3000; // 强制停止，等待正在执行的任务完成的超时时间，单位ms

	// 网络小数据请求操作的超时时间，如果是超过预定时间，将停止其操作
	private static final long TINY_HTTP_THREAD_POOL_TIMEOUT = 6000; // unit:ms
	// 本地cache操作的超时时间，如果是超过预定时间，将停止其操作
	private static final long CACHE_THREAD_POOL_TIMEOUT = 1000; // unit:ms
	

	public NdExecutorService() {
		newTinyHttpThreadPool();
		newCachedThreadPool();
		newFileUploadDownloadThreadPool();
		newBackgroundThreadPool();
	}
	
	public ExecutorService getCacheThreadPool() {
		return mCachedThreadPool;
	}

	public ExecutorService getTinyHttpThreadPool() {
		return mTinyHttpThreadPool;
	}
	
	public ExecutorService getFileUploadDownloadThreadPool() {
		return mFileUploadDownloadThreadPool;
	}
	
	public ExecutorService getBackgroundThreadPool() {
		return mBackgroundThreadPool;
	}

	/**
	 * 本地缓存线程池适用于一些加载本地缓存的快速操作。比如读取数据库，加载配置文件，读取本地文件等。
	 * 这些操作的用时预期可能会高于UI线程的执行超时时间，有存在ANR的危险，要么就是会造成UI界面不流畅
	 * 的问题。所以这些操作理应放到线程中执行。但是这些线程的操作时间很短暂，而且一般都很迫切，所以
	 * 放在单独的线程池中，给以较高的线程优先级，以便让它更快的执行完成
	 * 
	 * @return 本地缓存数据请求线程池
	 */
	private ExecutorService newCachedThreadPool() {
		mCachedThreadPool = new NdThreadPoolExecutor(12, 13, 0L,
				TimeUnit.SECONDS, mCacheWorkQueue,
				NdThreadFactory.newCachedThreadFactory());
		mCachedThreadPool.setThreadTimeout(CACHE_THREAD_POOL_TIMEOUT);
		return mCachedThreadPool;
	}

	/**
	 * UI交互密切相关的线程，比如获取历史聊天记录，刷新微博。这个线程池中的任务大部分应该是
	 * 跟服务器大交道的，而且都应该是一些简单的Rqeust_Response。不设计长时间的连接，
	 * 数据同步请求。这些线程的特点应该是单一，速度慢于缓存线程池。工程中的大部分任务应该都属于
	 * 这种类型。所以这个线程在设计的时候要考虑多个任务执行，以及多个任务可能造成的线程堵塞。 TODO
	 * 等待执行队列的大小，核心线程数，最大线程数。这三个参数还需要实际运行中再进行调整， 现在的大小定义之用于测试方便
	 * 
	 * @return
	 */
	private ExecutorService newTinyHttpThreadPool() {
		mTinyHttpThreadPool = new NdThreadPoolExecutor(2, 12, 0,
				TimeUnit.SECONDS, mTinyHttplWorkQueue,
				NdThreadFactory.newTinyHttpThreadFactory());
		mTinyHttpThreadPool.setThreadTimeout(TINY_HTTP_THREAD_POOL_TIMEOUT);
		return mTinyHttpThreadPool;
	}

	/**
	 * 
	 * @return : 文件上传下载操作线程池
	 */
	private ExecutorService newFileUploadDownloadThreadPool() {
		// TODO 是否需要对上传下载队列大小做限制？
		mFileUploadDownloadThreadPool = new NdThreadPoolExecutor(1, 1, 0,
				TimeUnit.SECONDS, mArrayBlockWorkQueue,
				NdThreadFactory.newUploadDownloadThreadFactory());
		return mFileUploadDownloadThreadPool;
	}

	/**
	 * @return 后台数据同步线程池
	 */
	private ExecutorService newBackgroundThreadPool() {
		mBackgroundThreadPool = new DuplicateCheckNdThreadPoolExecutor(1, 1, 0,
				TimeUnit.SECONDS, mArrayBlockWorkQueue,
				NdThreadFactory.newBackgroundThreadFactory());
		return mBackgroundThreadPool;
	}

	/**
	 * 线程池用于创建线程的工厂类,每个类型的线程池内的线程优先级不一样
	 */
	static class NdThreadFactory implements ThreadFactory {
		private final ThreadGroup group;
		private final AtomicInteger threadNumber = new AtomicInteger(1);
		private final String namePrefix;
		private int priority;

		private NdThreadFactory(int priority, String poolName) {
			SecurityManager s = System.getSecurityManager();
			group = (s != null) ? s.getThreadGroup() : Thread.currentThread()
					.getThreadGroup();
			namePrefix = "pool-" + poolName + "-thread-";
			this.priority = priority;
		}

		public static NdThreadFactory newBackgroundThreadFactory() {
			return new NdThreadFactory(Thread.MIN_PRIORITY, "backround");
		}

		public static NdThreadFactory newUploadDownloadThreadFactory() {
			return new NdThreadFactory(Thread.NORM_PRIORITY,
					"file_upload_download");
		}

		public static NdThreadFactory newTinyHttpThreadFactory() {
			return new NdThreadFactory(Thread.NORM_PRIORITY, "tiny_http");
		}

		public static NdThreadFactory newCachedThreadFactory() {
			return new NdThreadFactory(Thread.MAX_PRIORITY, "cached");
		}

		public Thread newThread(Runnable r) {
			Thread t = new Thread(group, r, namePrefix
					+ threadNumber.getAndIncrement(), 0);
			if (t.isDaemon()) {
				t.setDaemon(false);
			}

			t.setPriority(priority);
			return t;
		}
	}

	/**
	 * 退出所有的工作线程，这个函数是阻塞的
	 * 
	 * @return tiemout参数暂时没用
	 */
	public boolean awaitQuit(long timeout) {

		// 两种方式-轮询or阻塞。这里采用轮询的方式，减少线程数量以及线程之间的通信，
		// 减少代码复杂度
		// 停止接收新的请求，等待执行完退出
		shutdown();
		if (checkShutdown()) {
			return true;
		}

		int queryCount = 0;
		while (queryCount < (AWAIT_TERMINATION_SELF / 1000)) {
			try {
				Thread.sleep(1000); // 轮询的间隔是300ms
			} catch (InterruptedException e) {
				shutdownNow();
				Thread.currentThread().interrupt();
			}

			if (checkShutdown()) {
				mIsQuit = true;
				return true;
			}
			queryCount++;
		}

		// 超时，立即关闭
		shutdownNow();
		checkShutdown();
		queryCount = 0;
		while (queryCount < AWAIT_TERMINATION_FORCE / 1000) {
			try {
				Thread.sleep(1000); // 轮询的间隔是300ms
			} catch (InterruptedException e) {
				shutdownNow();
				Thread.currentThread().interrupt();
			}

			if (checkShutdown()) {
				mIsQuit = true;
				return true;
			}
			queryCount++;
		}
		
		mIsQuit = checkAndLog();;
		return mIsQuit;
	}
	
	public boolean isQuit() {
		return mIsQuit;
	}

	/**
	 * 参见ThreadPoolExecutor.shutdown()
	 * 
	 */
	private void shutdown() {
		if (!mTinyHttpThreadPool.isShutdown()) {
			mTinyHttpThreadPool.shutdown();
		}

		if (!mCachedThreadPool.isShutdown()) {
			mCachedThreadPool.shutdown();
		}

		if (!mFileUploadDownloadThreadPool.isShutdown()) {
			mFileUploadDownloadThreadPool.shutdown();
		}

		if (!mBackgroundThreadPool.isShutdown()) {
			mBackgroundThreadPool.shutdown();
		}
	}

	/**
	 * 参见ThreadPoolExecutor.shutdownNow()
	 * 
	 */
	private void shutdownNow() {
		mTinyHttpThreadPool.shutdownNow();
		mCachedThreadPool.shutdownNow();
		mFileUploadDownloadThreadPool.shutdownNow();
		mBackgroundThreadPool.shutdownNow();
	}

	private boolean checkShutdown() {
		return mTinyHttpThreadPool.isTerminated()
				&& mCachedThreadPool.isTerminated()
				&& mFileUploadDownloadThreadPool.isTerminated()
				&& mBackgroundThreadPool.isTerminated();
	}

	private boolean checkAndLog() {
		return mTinyHttpThreadPool.checkTerminatedAndLog()
				&& mCachedThreadPool.checkTerminatedAndLog()
				&& mFileUploadDownloadThreadPool.checkTerminatedAndLog()
				&& mBackgroundThreadPool.checkTerminatedAndLog();

	}

	/**
	 * 退出线程
	 * 
	 * @return
	 */
	private static boolean quitInterval(ThreadPoolExecutor pool) {
		pool.shutdown(); // Disable new tasks from being submitted
		try {
			// Wait a while for existing tasks to terminate
			if (!pool.awaitTermination(3, TimeUnit.SECONDS)) {
				pool.shutdownNow(); // Cancel currently executing tasks
				// Wait a while for tasks to respond to being cancelled
				if (!pool.awaitTermination(20, TimeUnit.SECONDS)) {
					System.err.println("Pool did not terminate");
					return false;
				}

			}

		} catch (InterruptedException ie) {
			// (Re-)Cancel if current thread also interrupted
			pool.shutdownNow();
			// Preserve interrupt status
			Thread.currentThread().interrupt();
		}
		return true;
	}

}
